package com.my.lucky.service.impl;

import com.my.lucky.config.IwencaiConfig;
import com.my.lucky.entity.StockLimitUp;
import com.my.lucky.entity.StockQueryRecord;
import com.my.lucky.entity.SysInfo;
import com.my.lucky.model.ReviewInfo;
import com.my.lucky.model.Stock;
import com.my.lucky.model.Stock60Boll;
import com.my.lucky.model.dto.StockSummaryDTO;
import com.my.lucky.repository.*;
import com.my.lucky.service.IWencaiServiceTools;
import com.my.lucky.service.StockService;
import com.my.lucky.service.StockSidewaysDetector;
import com.my.lucky.util.BollingerBandCalculator;
import com.my.lucky.util.DataUtil;
import com.my.lucky.util.DateUtils;
import com.my.lucky.util.PinyinUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StockServiceImpl implements StockService {

    @Autowired
    private final StockRepository stockRepository;

    @Autowired
    private Stock60BollRepository stock60BollRepository;

    @Autowired
    private StockQueryRecordRepository stockQueryRecordRepository;

    @Autowired
    private ReviewInfoRepository reviewInfoRepository;;

    @Autowired
    private IwencaiConfig iwencaiConfig;


    @Autowired
    private IWencaiServiceTools wencaiServiceTools;


    @Autowired
    private SysInfoRepository sysInfoRepository;


    @Autowired
    public StockServiceImpl(StockRepository stockRepository) {
        this.stockRepository = stockRepository;
    }

    /**
     * 查询CODE的记录
     * @param code      股票代码
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    @Override
    public List<StockSummaryDTO> findByCodeAndDateRange(String code, LocalDate startDate, LocalDate endDate) {
        List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(code, LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
        if (allStock == null || allStock.size() < 1) {
            allStock = stockRepository.findByNameAndCreatedAtBetweenOrderByTradeDateAsc(code, LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
        }
        List<StockSummaryDTO> result = new ArrayList<>();
//        System.out.println(allStock.size());
        if (allStock.isEmpty()) {
            return result; // 空列表直接返回
        }
        // 按创建时间降序排序，确保最新数据在前面
        allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

        // 直接初始化数组元素，长度由元素个数决定
        int[] arr = new int[]{5, 10, 20, 30, 40, 60};

        int index = 20;
        if (allStock != null && allStock.size() < index) {
            index = allStock.size();
        }

        if (allStock != null && allStock.size() >= index) {
//            System.out.println(allStock);

            Stock stock = allStock.get(0);
            for (int i = 0; i < arr.length; i++) {
                int days = arr[i];
                if (days > allStock.size()) {
                    continue;
                }
                LocalDateTime latestTime = stock.getCreatedAt();
                if (latestTime == null) {
                    // 如果没有创建时间，使用当前时间作为基准
                    latestTime = LocalDateTime.now();
                }

                StockSummaryDTO summaryDTO = new StockSummaryDTO(stock.getCode());
                // 设置基础信息
                summaryDTO.setName(stock.getName());
                summaryDTO.setConcept(stock.getConcept());
                summaryDTO.setCode(stock.getCode());
                summaryDTO.setNamepy(null);
                summaryDTO.setConceptpy("最近" + days + "天");
                summaryDTO.setChangePercent(stock.getChangePercent());
                summaryDTO.setPrice(stock.getClose());
                summaryDTO.setMarketValue(stock.getMarketValue());
                summaryDTO.setMainCost(stock.getMainCost());
                summaryDTO.setCurrentRankPosition(stock.getRankPosition());
                summaryDTO.setCurrentOldRankPosition(stock.getOldRankPosition());

                summaryDTO.setTurnoverRate(stock.getTurnoverRate());
                summaryDTO.setVolume(stock.getVolume());
                summaryDTO.setRsi6(stock.getRsi6());
                summaryDTO.setRsi12(stock.getRsi12());
                summaryDTO.setRsi24(stock.getRsi24());

                // 计算各时间区间的资金流向总和（每个区间都是7天）
                // 当天的7天内（0-7天前）
                summaryDTO.setMainCapitalFlow1d(calculateSumMainCapitalInRange(allStock, LocalDateTime.of(endDate, LocalTime.MAX), 0, days - 1));
                summaryDTO.setCapitalFlow1d(calculateSumCapitalInRange(allStock, LocalDateTime.of(endDate, LocalTime.MAX), 0, days - 1));
                result.add(summaryDTO);
            }

            for (int i = 0; i < index; i++) {
                // 获取最新记录的创建时间作为基准
                Stock latestStock = allStock.get(i);
                LocalDateTime latestTime = latestStock.getCreatedAt();
                if (latestTime == null) {
                    // 如果没有创建时间，使用当前时间作为基准
                    latestTime = LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MAX);
                } else {
                    latestTime = LocalDateTime.of(latestTime.toLocalDate(), LocalTime.MAX);
                }

                StockSummaryDTO summaryDTO = new StockSummaryDTO(stock.getCode());

                // 设置基础信息
                summaryDTO.setName(latestStock.getName());
                summaryDTO.setConcept(latestStock.getConcept());
                summaryDTO.setCode(stock.getCode());
                summaryDTO.setNamepy(null);
                summaryDTO.setConceptpy(latestStock.getDate());
                summaryDTO.setChangePercent(latestStock.getChangePercent());
                summaryDTO.setPrice(latestStock.getClose());
                summaryDTO.setMarketValue(latestStock.getMarketValue());
                summaryDTO.setMainCost(latestStock.getMainCost());
                summaryDTO.setCurrentRankPosition(latestStock.getRankPosition());
                summaryDTO.setCurrentOldRankPosition(latestStock.getOldRankPosition());

                summaryDTO.setTurnoverRate(latestStock.getTurnoverRate());
                summaryDTO.setVolume(latestStock.getVolume());
                summaryDTO.setRsi6(latestStock.getRsi6());
                summaryDTO.setRsi12(latestStock.getRsi12());
                summaryDTO.setRsi24(latestStock.getRsi24());

                // 计算各时间区间的资金流向总和（每个区间都是7天）
                // 当天的7天内（0-7天前）
                summaryDTO.setMainCapitalFlow1d(calculateSumMainCapitalInRange(allStock, latestTime, i, 0));
                summaryDTO.setCapitalFlow1d(calculateSumCapitalInRange(allStock, latestTime, i, 0));

                // 1天前的7天（1-8天前）
                summaryDTO.setMainCapitalFlow2d(calculateSumMainCapitalInRange(allStock, latestTime, i, 1));
                summaryDTO.setCapitalFlow2d(calculateSumCapitalInRange(allStock, latestTime, i, 1));

                // 2天前的7天（2-9天前）
                summaryDTO.setMainCapitalFlow3d(calculateSumMainCapitalInRange(allStock, latestTime, i, 2));
                summaryDTO.setCapitalFlow3d(calculateSumCapitalInRange(allStock, latestTime, i, 2));

                // 3天前的7天（3-10天前）
                summaryDTO.setMainCapitalFlow5d(calculateSumMainCapitalInRange(allStock, latestTime, i, 4));
                summaryDTO.setCapitalFlow5d(calculateSumCapitalInRange(allStock, latestTime, i, 4));

                // 4天前的7天（4-11天前）
                summaryDTO.setMainCapitalFlow6d(calculateSumMainCapitalInRange(allStock, latestTime, i, 5));
                summaryDTO.setCapitalFlow6d(calculateSumCapitalInRange(allStock, latestTime, i, 5));

                // 5天前的7天（5-12天前）
                summaryDTO.setMainCapitalFlow7d(calculateSumMainCapitalInRange(allStock, latestTime, i, 6));
                summaryDTO.setCapitalFlow7d(calculateSumCapitalInRange(allStock, latestTime, i, 6));
                result.add(summaryDTO);
            }

        }
//        System.out.println(result);
//        result.sort(Comparator.comparing(StockSummaryDTO::getCreatedAt).reversed());
        return result;
    }

    /**
     * 30天主力进入
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    @Override
    public String checkAndDateRange(LocalDate startDate, LocalDate endDate, Map<String, Stock> kdjStocks) {
        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
        System.out.println(endDate + "=checkAndDateRange=" + daysAllStock.size());
        String res = "";

        List<StockQueryRecord> now10Drecords  = new ArrayList<>();
        now10Drecords.addAll(iwencaiConfig.getNow10Drecords());

        Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
        if (now10Drecords != null) {
            // 遍历列表，将stockCode作为键，对象作为值存入Map
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(), record);
                }
            }
        }

        for (int j = 0; j < daysAllStock.size(); j++) {
            Stock codestock = daysAllStock.get(j);

//            if (!codestock.getCode().startsWith("3")) {
//                continue; // 空列表直接返回
//            }

            if (kdjStocks == null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
                continue; // 空列表直接返回
            }

            if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
                continue; // 空列表直接返回
            }

            List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));

//          List<StockSummaryDTO> result = new ArrayList<>();
//            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
            if (allStock.isEmpty()) {
                return res; // 空列表直接返回
            }
            // 按创建时间降序排序，确保最新数据在前面
            allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

            // 直接初始化数组元素，长度由元素个数决定
//          int[] arr = new int[]{5, 10, 20, 30, 40, 60};
            int[] arr = new int[]{5, 10, 20};

            int index = 20;
            if (allStock != null && allStock.size() < index) {
                index = allStock.size();
            }

            boolean flag = true;
            if (allStock != null && allStock.size() > index) {
                Stock stock0 = allStock.get(0); // 今天的数据
                double profitPercent = 0.0;

                if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                    profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                }
                //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
//                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                boolean mainFlag = (stock0.getMainCapitalFlow() != null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(0)) > 0);
                if (stock0 != null && stock0.getTurnoverRate() > 1
                        && stock0.getTurnoverRate()
                        <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                        && profitPercent < 0.2
//                        && stock0.getChangePercent()>0 //涨幅大于0
//                        && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
                        && mainFlag) {
//                    flag = true;
                    List<Stock> threeDaysData = new ArrayList<>();
                    threeDaysData.add(stock0);
                    threeDaysData.add(allStock.get(1));
                    threeDaysData.add(allStock.get(2));
                    BollingerBandCalculator calculator = new BollingerBandCalculator();
                    BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.04);
//                    System.out.println("todayResult=="+todayResult);
                    if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                        flag = true;
                    } else {
                        flag = false;
                    }
                } else {
                    flag = false;
                }

                for (int i = 0; i < arr.length; i++) {
                    int days = arr[i];
                    if (days > allStock.size()) {
                        continue;
                    }
                    if (!flag) {
                        continue;
                    }
                    // 计算各时间区间的资金流向总和（每个区间都是7天）
                    // 当天的7天内（0-7天前）
                    BigDecimal mark = new BigDecimal("10000"); //万 TODO
                    BigDecimal main = calculateSumMainCapitalInRange(allStock, LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MAX), 0, days - 1);
                    BigDecimal capital = calculateSumCapitalInRange(allStock, LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MAX), 0, days - 1);
//                    System.out.println("codestock.getCode()=="+codestock.getCode()+"=days=="+days+"=main=="+main+"=allStock.size=="+allStock.size());
                    if (main.compareTo(mark) < 0) {
                        flag = false;
                        continue;
                    }
                }
                if (flag) {
                    res += codestock.getCode() + ",";
                }
            }
        }


//        if (res!=null && !res.trim().isEmpty()){
        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("40天主力进");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//
//        }
        System.out.println("checkAndDateRange res==" + res);

//        System.out.println(result);
//        result.sort(Comparator.comparing(StockSummaryDTO::getCreatedAt).reversed());
        return res;
    }

    /**
     * 30天主力进入 洗盘
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    @Override
    public String checkAndDateRangeWash(LocalDate startDate, LocalDate endDate, Map<String, Stock> kdjStocks) {
        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
        System.out.println(endDate + "=checkAndDateRangeWash=" + daysAllStock.size());

        List<StockQueryRecord> now10Drecords  = new ArrayList<>();
        if (now10Drecords==null){
            now10Drecords = new ArrayList<>();
        }
        now10Drecords.addAll(iwencaiConfig.getNow10Drecords());

        Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
        if (now10Drecords != null) {
            // 遍历列表，将stockCode作为键，对象作为值存入Map
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(), record);
                }
            }
        }

        String res = "";
        for (int j = 0; j < daysAllStock.size(); j++) {
            Stock codestock = daysAllStock.get(j);

//            if (!codestock.getCode().startsWith("3")) {
//                continue; // 空列表直接返回
//            }

//            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
//                continue; // 空列表直接返回
//            }

            if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
                continue; // 空列表直接返回
            }

            List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
//        List<StockSummaryDTO> result = new ArrayList<>();
//            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
            if (allStock.isEmpty()) {
                return res; // 空列表直接返回
            }
            // 按创建时间降序排序，确保最新数据在前面
            allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

            // 直接初始化数组元素，长度由元素个数决定
//          int[] arr = new int[]{5, 10, 20, 30, 40, 60};
            int[] arr = new int[]{5, 10, 20};

            int index = 20;
            if (allStock != null && allStock.size() < index) {
                index = allStock.size();
            }

            boolean flag = true;
            if (allStock != null && allStock.size() >= index) {
                Stock stock0 = allStock.get(0); // 今天的数据
                Stock stock1 = allStock.get(1); // 今天的数据
                double profitPercent = 0.0;

                if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                    profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                }
                //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
//                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
//                boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(0))>0);
                if (stock0 != null && stock0.getTurnoverRate() > 1 && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                        && profitPercent < 0.2
                        && stock1.getMainCapitalFlow().compareTo(new BigDecimal(10000 * 10000)) > 0

                        && stock0.getMainCapitalFlow().compareTo(new BigDecimal(0)) < 0
                        && stock0.getMainCapitalFlow().abs().compareTo(stock1.getMainCapitalFlow()) < 0
                        && stock0.getClose()>stock0.getLow() //收盘大于开盘价
                        && stock0.getCapitalFlow().compareTo(new BigDecimal(0)) < 0 //散户也再跑路的
                        && stock0.getMainCapitalFlow().abs().compareTo(stock0.getCapitalFlow().abs()) < 0 //散户也再跑路的
//                        && stock0.getChangePercent()>0 //涨幅大于0
//                        && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
//                        && mainFlag
                ) {
//                    flag = true;
                    List<Stock> threeDaysData = new ArrayList<>();
                    threeDaysData.add(stock0);
                    threeDaysData.add(allStock.get(1));
                    threeDaysData.add(allStock.get(2));
                    BollingerBandCalculator calculator = new BollingerBandCalculator();
                    BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.001);
                    if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                        flag = true;
                    } else {
                        flag = false;
                    }
                } else {
                    flag = false;
                }

                for (int i = 0; i < arr.length; i++) {
                    int days = arr[i];
                    if (days > allStock.size()) {
                        continue;
                    }
                    if (!flag) {
                        continue;
                    }
                    // 计算各时间区间的资金流向总和（每个区间都是7天）
                    // 当天的7天内（0-7天前）
                    BigDecimal mark = new BigDecimal("10000"); //万 TODO
                    BigDecimal main = calculateSumMainCapitalInRange(allStock, LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MAX), 0, days - 1);
                    BigDecimal capital = calculateSumCapitalInRange(allStock, LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MAX), 0, days - 1);
//                    System.out.println("codestock.getCode()=="+codestock.getCode()+"=days=="+days+"=main=="+main+"=allStock.size=="+allStock.size());
                    if (main.compareTo(mark) < 0) {
                        flag = false;
                        continue;
                    }
                }
                if (flag) {
                    res += codestock.getCode() + ",";
                }
            }
        }


//        if (res!=null && !res.trim().isEmpty()){
        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("30天主力进洗盘");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//
//        }
        System.out.println("checkAndDateRangeWash res==" + res);

//        System.out.println(result);
//        result.sort(Comparator.comparing(StockSummaryDTO::getCreatedAt).reversed());
        return res;
    }

    /**
     * boll扩张
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    @Override
    public String checkAndDateRangeBoll(LocalDate startDate, LocalDate endDate, Map<String, Stock> kdjStockss) {
        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
//        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAsc(endDate, endDate,"002291");
        ReviewInfo reviewInfo = reviewInfoRepository.findFirstByReviewTime(endDate);
        String gainian="";
        if (reviewInfo!=null) {
            gainian=DataUtil.processConcepts(reviewInfo.getReviewGainian());
        }
        System.out.println(endDate + "=checkAndDateRangeBoll=" + daysAllStock.size());
//        System.out.println(endDate + "=checkAndDateRangeBoll reviewInfo=" + reviewInfo+"========"+gainian);

        // 处理字符串并获取集合
        Set<String> Reviewset = DataUtil.processFirstString(gainian);

//        List<StockQueryRecord> now10Drecords  = stockQueryRecordRepository.findByQueryTimeBetweenOrderByQueryTimeAsc(LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MIN), LocalDateTime.of(LocalDate.now(), LocalTime.MAX));//今日查询
        List<StockQueryRecord> now10Drecords  = new ArrayList<>();
        if (now10Drecords==null){
            now10Drecords = new ArrayList<>();
        }
        now10Drecords.addAll(iwencaiConfig.getNow10Drecords());

        Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
        if (now10Drecords != null) {

//            if(iwencaiConfig.getNowTodayDrecords()!=null){ //当天扩张的
//                now10Drecords.addAll(iwencaiConfig.getNowTodayDrecords());
//            }
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(), record);
                }
            }
        }

        String res = "";
        for (int j = 0; j < daysAllStock.size(); j++) {
            Stock codestock = daysAllStock.get(j);

//            if (!codestock.getCode().startsWith("3")) {
//                continue; // 空列表直接返回
//            }

//            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
//                continue; // 空列表直接返回
//            }

            if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
                continue; // 空列表直接返回
            }

            List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
//            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
            if (allStock.isEmpty()) {
                return res; // 空列表直接返回
            }
            // 按创建时间降序排序，确保最新数据在前面
            allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

            int index = 20;
            if (allStock != null && allStock.size() < index) {
                index = allStock.size();
            }

            boolean flag = true;
            if (allStock != null && allStock.size() >= index && index>3) {
                Stock stock0 = allStock.get(0); // 今天的数据
                Stock stock1 = allStock.get(1); // 今天的数据
                Stock stock2 = allStock.get(2); // 今天的数据
                Stock stock3 = allStock.get(3); // 今天的数据
//                Stock stock4 = allStock.get(4); // 今天的数据

                List<Stock> stockList = new ArrayList<>();
                stockList.add(stock0);
                stockList.add(stock1);
                stockList.add(stock2);
                stockList.add(stock3);
//                stockList.add(stock4);

//                Stock stock5 = allStock.get(5); // 今天的数据
//                Stock stock6 = allStock.get(6); // 今天的数据
//                Stock stock7 = allStock.get(7); // 今天的数据
//                Stock stock8 = allStock.get(8); // 今天的数据
//                Stock stock9 = allStock.get(9); // 今天的数据
//
//                stockList.add(stock5);
//                stockList.add(stock6);
//                stockList.add(stock7);
//                stockList.add(stock8);
//                stockList.add(stock9);

                double profitPercent = 0.0;

                Set<String> stockset = DataUtil.processSecondString(stock0.getConcept());

                // 检查交集
                boolean hasIntersection = DataUtil.checkIntersection(Reviewset, stockset);

                if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                    profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                }
                //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
//                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                boolean mainstockFlag = allMatchConditionStock(stock0);
                boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
//                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
//                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                if (stock0 != null
                        && mainstockFlag //主力流入或散户流出多
//                        && mainFlag //当天主力进入千万
                        && hasIntersection //有关联概念
                        && main5DFlag //5天主力进入千万
//                        && main10DFlag //10天主力进入千万
                        && mainCost5DFlag //5天主力成本都是再增加
//                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                        && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                        && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率

//                        && stock0.getChangePercent()<=4 //涨幅在-5 - 4%之间
//                        && stock0.getChangePercent()>=-5 //涨幅在-5 - 4%之间

//                        && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升

                ) {

                    List<Stock> threeDaysData = new ArrayList<>();
                    List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(codestock.getCode(),DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
                    if (s60bolls!=null){
                        List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
                        //                        System.out.println("s60bollsToStock=="+s60bollsToStock);
                        if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
                            threeDaysData.add(s60bollsToStock.get(0));
                            threeDaysData.add(s60bollsToStock.get(1));
                            threeDaysData.add(s60bollsToStock.get(2));

                            BollingerBandCalculator calculator = new BollingerBandCalculator();
//                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.01);
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayLimitStatus(threeDaysData, 0.01);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }else{
                            flag = false;
                        }
                    }else{
                        flag = false;
                    }
//日开口
                        if (flag) {
//                    List<Stock> threeDaysData = new ArrayList<>();
//                    threeDaysData.add(stock0);
//                    threeDaysData.add(stock1);
//                    threeDaysData.add(stock2);
                            BollingerBandCalculator calculator = new BollingerBandCalculator();
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judge3DStatus(stockList, 0.015);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }
                } else {
                    flag = false;
                }

                if (flag) {
                    res += codestock.getCode() + ",";
                }
            }
        }

        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("Boll扩张-连续");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//

        System.out.println("checkAndDateRangeBoll res==" + res);

        iwencaiConfig.setFocusBoll(iwencaiConfig.getFocusBoll()+"<br>连续扩张:"+res);
        return res;
    }
   /**
     * boll扩张
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    @Override
    public String checkAndDateRangeBoll20cm(LocalDate startDate, LocalDate endDate, Map<String, Stock> kdjStockss) {
        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
//        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAsc(endDate, endDate,"002291");
        ReviewInfo reviewInfo = reviewInfoRepository.findFirstByReviewTime(endDate);
        String gainian="";
        if (reviewInfo!=null) {
            gainian=DataUtil.processConcepts(reviewInfo.getReviewGainian());
        }
        System.out.println(endDate + "=checkAndDateRangeBoll20cm=" + daysAllStock.size());
//        System.out.println(endDate + "=checkAndDateRangeBoll reviewInfo=" + reviewInfo+"========"+gainian);

        // 处理字符串并获取集合
        Set<String> Reviewset = DataUtil.processFirstString(gainian);

//        List<StockQueryRecord> now10Drecords  = stockQueryRecordRepository.findByQueryTimeBetweenOrderByQueryTimeAsc(LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MIN), LocalDateTime.of(LocalDate.now(), LocalTime.MAX));//今日查询
        List<StockQueryRecord> now10Drecords  = new ArrayList<>();
        now10Drecords.addAll(iwencaiConfig.getNow10Drecords());

        Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
        if (now10Drecords != null) {
            // 遍历列表，将stockCode作为键，对象作为值存入Map
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(), record);
                }
            }
        }

        String res = "";
        for (int j = 0; j < daysAllStock.size(); j++) {
            Stock codestock = daysAllStock.get(j);

            if (!codestock.getCode().startsWith("3")  && !codestock.getCode().startsWith("68") && !codestock.getCode().startsWith("92")) {
                continue; // 空列表直接返回
            }

//            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
//                continue; // 空列表直接返回
//            }

            if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
                continue; // 空列表直接返回
            }

            List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
//            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
            if (allStock.isEmpty()) {
                return res; // 空列表直接返回
            }
            // 按创建时间降序排序，确保最新数据在前面
            allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

            int index = 20;
            if (allStock != null && allStock.size() < index) {
                index = allStock.size();
            }

            boolean flag = true;
            if (allStock != null && allStock.size() >= index) {
                Stock stock0 = allStock.get(0); // 今天的数据
                Stock stock1 = allStock.get(1); // 今天的数据
                Stock stock2 = allStock.get(2); // 今天的数据
                Stock stock3 = allStock.get(3); // 今天的数据
                Stock stock4 = allStock.get(4); // 今天的数据

                List<Stock> stockList = new ArrayList<>();
                stockList.add(stock0);
                stockList.add(stock1);
                stockList.add(stock2);
//                stockList.add(stock3);
//                stockList.add(stock4);

//                Stock stock5 = allStock.get(5); // 今天的数据
//                Stock stock6 = allStock.get(6); // 今天的数据
//                Stock stock7 = allStock.get(7); // 今天的数据
//                Stock stock8 = allStock.get(8); // 今天的数据
//                Stock stock9 = allStock.get(9); // 今天的数据
//
//                stockList.add(stock5);
//                stockList.add(stock6);
//                stockList.add(stock7);
//                stockList.add(stock8);
//                stockList.add(stock9);

                double profitPercent = 0.0;
                double highCPercent = 0.0;//最高价与现价征服

                Set<String> stockset = DataUtil.processSecondString(stock0.getConcept());

                // 检查交集
//                boolean hasIntersection = DataUtil.checkIntersection(Reviewset, stockset);

                if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                    profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                }

                highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
//                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                boolean mainstockFlag = allMatchConditionStock(stock0);
                boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
//                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
//                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
//                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                if (stock0 != null
                        && mainstockFlag //主力流入或散户流出多
                        && mainFlag //当天主力进入十万
//                        && hasIntersection //有关联概念
                        && mainCost5DFlag //5天主力成本都是再增加
//                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                        && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                        && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率
                        && stock0.getChangePercent()<=5.5 && stock0.getChangePercent()>=0.5 //涨幅在2~4%之间
                        && highClostFlag //大于开盘价
                        && Math.abs(highCPercent)<=3 //振幅3%以内
                        && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
                ) {
                    List<Stock> threeDaysData = new ArrayList<>();
                    threeDaysData.add(stock0);
                    threeDaysData.add(stock1);
                    threeDaysData.add(stock2);
                    BollingerBandCalculator calculator = new BollingerBandCalculator();
                    BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.01);
                    if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                        flag = true;
                    } else {
                        flag = false;
                    }
                } else {
                    flag = false;
                }

                if (flag) {
                    res += codestock.getCode() + ",";
                }
            }
        }

        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("Boll扩张-20CM");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//

        System.out.println("checkAndDateRangeBoll20cm===res==" + res);
        return res;
    }

   /**
     * boll扩张
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    @Override
    public String checkAndDateRangeBoll10cm(LocalDate startDate, LocalDate endDate, Map<String, Stock> kdjStockss) {
        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
        List<StockQueryRecord> now10Drecords  = new ArrayList<>();
        now10Drecords.addAll(iwencaiConfig.getNow10Drecords());

        Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
        if (now10Drecords != null) {
            // 遍历列表，将stockCode作为键，对象作为值存入Map
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(), record);
                }
            }
        }

        String res = "";
        for (int j = 0; j < daysAllStock.size(); j++) {
            Stock codestock = daysAllStock.get(j);

            if (!codestock.getCode().startsWith("0")  && !codestock.getCode().startsWith("60") ) {
                continue; // 空列表直接返回
            }

//            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
//                continue; // 空列表直接返回
//            }

            if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
                continue; // 空列表直接返回
            }

            List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
//            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
            if (allStock.isEmpty()) {
                return res; // 空列表直接返回
            }
            // 按创建时间降序排序，确保最新数据在前面
            allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

            int index = 20;
            if (allStock != null && allStock.size() < index) {
                index = allStock.size();
            }

            boolean flag = true;
            if (allStock != null && allStock.size() >= index  && index>2) {
                Stock stock0 = allStock.get(0); // 今天的数据
                Stock stock1 = allStock.get(1); // 今天的数据
                Stock stock2 = allStock.get(2); // 今天的数据
//                Stock stock3 = allStock.get(3); // 今天的数据
//                Stock stock4 = allStock.get(4); // 今天的数据

                List<Stock> stockList = new ArrayList<>();
                stockList.add(stock0);
                stockList.add(stock1);
                stockList.add(stock2);
//                stockList.add(stock3);
//                stockList.add(stock4);

//                Stock stock5 = allStock.get(5); // 今天的数据
//                Stock stock6 = allStock.get(6); // 今天的数据
//                Stock stock7 = allStock.get(7); // 今天的数据
//                Stock stock8 = allStock.get(8); // 今天的数据
//                Stock stock9 = allStock.get(9); // 今天的数据
//
//                stockList.add(stock5);
//                stockList.add(stock6);
//                stockList.add(stock7);
//                stockList.add(stock8);
//                stockList.add(stock9);

                double profitPercent = 0.0;
                double highCPercent = 0.0;//最高价与现价征服

//                Set<String> stockset = DataUtil.processSecondString(stock0.getConcept());

                // 检查交集
//                boolean hasIntersection = DataUtil.checkIntersection(Reviewset, stockset);

                if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                    profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                }

                highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
//                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                boolean mainstockFlag = allMatchConditionStock(stock0);
                boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
//                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
//                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
//                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                if (stock0 != null
                        && mainstockFlag //主力流入或散户流出多
                        && mainFlag //当天主力进入十万
//                        && hasIntersection //有关联概念
                        && mainCost5DFlag //5天主力成本都是再增加
//                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                        && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                        && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率
                        && stock0.getChangePercent()<=4.5 && stock0.getChangePercent()>=0.5 //涨幅在0.5~4.5%之间
                        && highClostFlag //大于开盘价
                        && Math.abs(highCPercent)<=3 //振幅3%以内
                        && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
                ) {
                    //60分boll扩张
                    List<Stock> threeDaysData = new ArrayList<>();
                    List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(codestock.getCode(),DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
                    if (s60bolls!=null){
                        List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
//                        System.out.println("s60bollsToStock=="+s60bollsToStock);
                        if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
                            threeDaysData.add(s60bollsToStock.get(0));
                            threeDaysData.add(s60bollsToStock.get(1));
//                            threeDaysData.add(s60bollsToStock.get(2));
                            BollingerBandCalculator calculator = new BollingerBandCalculator();
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.03);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }else{
                            flag = false;
                        }
                    }else{
                        flag = false;
                    }

                    //日开口
                    if (flag) {
                        BollingerBandCalculator calculator = new BollingerBandCalculator();
                        BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(stockList, 0.01);
                        if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                            flag = true;
                        } else {
                            flag = false;
                        }
                    }
//                    log.info("checkAndDateRangeBoll10cm threeDaysData=="+threeDaysData+"==="+codestock.getCode());
                } else {
                    flag = false;
                }

                if (flag) {
                    res += codestock.getCode() + ",";
                }
            }
        }
        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("Boll扩张-10CM");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//

        log.info("checkAndDateRangeBoll10cm===res==" + res);
        return res;
    }

    @Override
    public String checkAndDateRangeBollFlow(LocalDate startDate, LocalDate endDate) {
        String res = "";
        Map<String, String> codestockMap = new HashMap<>();
        String flowstock = iwencaiConfig.getFocus();

        String[] flowstocks = new String[0];
        if (flowstock != null && !flowstock.isEmpty()) {
            // 分割并过滤空字符串
            flowstocks = Arrays.stream(flowstock.split(","))
                    .filter(s -> !s.isEmpty())
                    .toArray(String[]::new);

            for (int i = 0; i < flowstocks.length; i++) {
                codestockMap.put(flowstocks[i], flowstocks[i]);
            }
        }

        Set<String> keySet = codestockMap.keySet();
        for (String code : keySet) {
//        for (int i = 0; i <codestockMap.size() ; i++) {

//            String code=key;
//            System.out.println("checkAndDateRangeBollFlow--code==="+code);
    //        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
//            List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAsc(LocalDate.now(), LocalDate.now(),code);
//
//            for (int j = 0; j < daysAllStock.size(); j++) {
//                Stock codestock = daysAllStock.get(j);

    //            if (!codestock.getCode().startsWith("0")  && !codestock.getCode().startsWith("60") ) {
    //                continue; // 空列表直接返回
    //            }

    //            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
    //                continue; // 空列表直接返回
    //            }

//                if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
//                    continue; // 空列表直接返回
//                }

                List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(code, LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(LocalDate.now(), LocalTime.MAX));
    //            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
                if (allStock.isEmpty()) {
                    continue;
//                    return res; // 空列表直接返回
                }
                // 按创建时间降序排序，确保最新数据在前面
                allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

                int index = 20;
                if (allStock != null && allStock.size() < index) {
                    index = allStock.size();
                }

                boolean flag = true;
                if (allStock != null && allStock.size() >= index && allStock.size()>2 ) {
                    Stock stock0 = allStock.get(0); // 今天的数据
                    Stock stock1 = allStock.get(1); // 今天的数据
                    Stock stock2 = allStock.get(2); // 今天的数据
    //                Stock stock3 = allStock.get(3); // 今天的数据
    //                Stock stock4 = allStock.get(4); // 今天的数据

                    List<Stock> stockList = new ArrayList<>();
                    stockList.add(stock0);
                    stockList.add(stock1);
                    stockList.add(stock2);
    //                stockList.add(stock3);
    //                stockList.add(stock4);

                    double profitPercent = 0.0;
                    double highCPercent = 0.0;//最高价与现价征服

    //                Set<String> stockset = DataUtil.processSecondString(stock0.getConcept());

                    // 检查交集
    //                boolean hasIntersection = DataUtil.checkIntersection(Reviewset, stockset);

                    if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                        profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                    }

                    highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                    //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
    //                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                    boolean mainstockFlag = allMatchConditionStock(stock0);
                    boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                    boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
    //                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
    //                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
    //                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                    if (stock0 != null
                            && mainstockFlag //主力流入或散户流出多
                            && mainFlag //当天主力进入十万
    //                        && hasIntersection //有关联概念
                            && mainCost5DFlag //5天主力成本都是再增加
    //                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
//                            && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                            && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率
                            && stock0.getChangePercent()<=4.5 && stock0.getChangePercent()>=0.1 //涨幅在0.5~4.5%之间
                            && highClostFlag //大于开盘价
                            && Math.abs(highCPercent)<=3 //振幅3%以内
                            && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
                    ) {
                        //60分boll扩张
                        List<Stock> threeDaysData = new ArrayList<>();
                        List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(code,DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
                        if (s60bolls!=null){
                            List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
    //                        System.out.println("s60bollsToStock=="+s60bollsToStock);
                            if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
                                threeDaysData.add(s60bollsToStock.get(0));
                                threeDaysData.add(s60bollsToStock.get(1));
    //                            threeDaysData.add(s60bollsToStock.get(2));
                                BollingerBandCalculator calculator = new BollingerBandCalculator();
                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.001);
                                if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                    flag = true;
                                } else {
                                    flag = false;
                                }
                            }else{
                                flag = false;
                            }
                        }else{
                            flag = false;
                        }

                        //日开口
                        if (flag) {
                            BollingerBandCalculator calculator = new BollingerBandCalculator();
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(stockList, 0.001);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }
//                        log.info("checkAndDateRangeBollFlow threeDaysData=="+threeDaysData+"==="+codestock.getCode());
                    } else {
                        flag = false;
                    }

                    if (flag) {
                        res += code + ",";
                    }
                }
//            }
        }
//        SysInfo sysInfo = new SysInfo();
//        sysInfo.setSysKey("Boll扩张-10CM");
//        sysInfo.setSysValue(res);
//        sysInfo.setTypes(1);
//        sysInfo.setCreateTime(LocalDateTime.now());
//        upsertSysInfo(sysInfo);//
        iwencaiConfig.setFocusBoll("关注概念:"+iwencaiConfig.getFocusGaiNian()+"<br>关注扩张:"+res);
        log.info("checkAndDateRangeBollFlow===res==" + res);
        return res;
    }

    @Override
    public String checkAndDateRangeBollSearch(LocalDate startDate, LocalDate endDate) {
        String res = "";
        Map<String, String> codestockMap = new HashMap<>();
//        String flowstock = iwencaiConfig.getFocus();
        List<StockQueryRecord> now10Drecords  = new ArrayList<>();
        now10Drecords.addAll(iwencaiConfig.getNow10Drecords());

//            Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
            if (now10Drecords != null) {
                // 遍历列表，将stockCode作为键，对象作为值存入Map
                for (StockQueryRecord record : now10Drecords) {
                    // 避免处理null对象和null的stockCode
                    if (record != null && record.getStockCode() != null) {
//                        flowstock+=","+record.getStockCode();
                        codestockMap.put(record.getStockCode(),record.getStockCode());
//                        stockMap.put(record.getStockCode(), record);
                    }
                }
            }

//        Map<String, String> codestockMap = new HashMap<>();
//        String[] flowstocks = new String[0];
//        if (flowstock != null && !flowstock.isEmpty()) {
//            // 分割并过滤空字符串
//            flowstocks = Arrays.stream(flowstock.split(","))
//                    .filter(s -> !s.isEmpty())
//                    .toArray(String[]::new);
//
//            for (int i = 0; i < flowstocks.length; i++) {
//                codestockMap.put(flowstocks[i], flowstocks[i]);
//            }
//
//        }
//        System.out.println(codestockMap);

        Set<String> keySet = codestockMap.keySet();
        for (String key : keySet) {
//        for (int i = 0; i <codestockMap.size() ; i++) {

            String code=key;
//            System.out.println("code==="+code);
    //        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
            List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAsc(endDate, endDate,code);

            for (int j = 0; j < daysAllStock.size(); j++) {
                Stock codestock = daysAllStock.get(j);

    //            if (!codestock.getCode().startsWith("0")  && !codestock.getCode().startsWith("60") ) {
    //                continue; // 空列表直接返回
    //            }

    //            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
    //                continue; // 空列表直接返回
    //            }

//                if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
//                    continue; // 空列表直接返回
//                }

                List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
    //            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
                if (allStock.isEmpty()) {
                    return res; // 空列表直接返回
                }
                // 按创建时间降序排序，确保最新数据在前面
                allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

                int index = 20;
                if (allStock != null && allStock.size() < index) {
                    index = allStock.size();
                }

                boolean flag = true;
                if (allStock != null && allStock.size() >= index  && index>2 ) {
                    Stock stock0 = allStock.get(0); // 今天的数据
                    Stock stock1 = allStock.get(1); // 今天的数据
                    Stock stock2 = allStock.get(2); // 今天的数据
    //                Stock stock3 = allStock.get(3); // 今天的数据
    //                Stock stock4 = allStock.get(4); // 今天的数据

                    List<Stock> stockList = new ArrayList<>();
                    stockList.add(stock0);
                    stockList.add(stock1);
                    stockList.add(stock2);
    //                stockList.add(stock3);
    //                stockList.add(stock4);

                    double profitPercent = 0.0;
                    double highCPercent = 0.0;//最高价与现价征服

    //                Set<String> stockset = DataUtil.processSecondString(stock0.getConcept());

                    // 检查交集
    //                boolean hasIntersection = DataUtil.checkIntersection(Reviewset, stockset);

                    if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                        profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                    }

                    highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                    //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
    //                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                    boolean mainstockFlag = allMatchConditionStock(stock0);
                    boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                    boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
//                    boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
    //                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
    //                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                    if (stock0 != null
                            && mainstockFlag //主力流入或散户流出多
                            && mainFlag //当天主力进入十万
    //                        && hasIntersection //有关联概念
                            && mainCost5DFlag //5天主力成本都是再增加
    //                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                            && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                            && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率
                            && stock0.getChangePercent()<=4.5 && stock0.getChangePercent()>=0.1 //涨幅在0.5~4.5%之间
                            && highClostFlag //大于开盘价
                            && Math.abs(highCPercent)<=3 //振幅3%以内
                            && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
                    ) {
                        //60分boll扩张
                        List<Stock> threeDaysData = new ArrayList<>();
                        List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(codestock.getCode(),DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
                        if (s60bolls!=null){
                            List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
    //                        System.out.println("s60bollsToStock=="+s60bollsToStock);
                            if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
                                threeDaysData.add(s60bollsToStock.get(0));
                                threeDaysData.add(s60bollsToStock.get(1));
    //                            threeDaysData.add(s60bollsToStock.get(2));
                                BollingerBandCalculator calculator = new BollingerBandCalculator();
                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.01);
                                if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                    flag = true;
                                } else {
                                    flag = false;
                                }
                            }else{
                                flag = false;
                            }
                        }else{
                            flag = false;
                        }

                        //日开口
                        if (flag) {
                            BollingerBandCalculator calculator = new BollingerBandCalculator();
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(stockList, 0.01);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }
//                        log.info("checkAndDateRangeBollSearch threeDaysData=="+threeDaysData+"==="+codestock.getCode());
                    } else {
                        flag = false;
                    }

                    if (flag) {
                        res += codestock.getCode() + ",";
                    }
                }
            }
        }
//        SysInfo sysInfo = new SysInfo();
//        sysInfo.setSysKey("Boll扩张-10CM");
//        sysInfo.setSysValue(res);
//        sysInfo.setTypes(1);
//        sysInfo.setCreateTime(LocalDateTime.now());
//        upsertSysInfo(sysInfo);//
        iwencaiConfig.setFocusBoll(iwencaiConfig.getFocusBoll()+"<br>"+"查询扩张:"+res);
        log.info("checkAndDateRangeBollSearch===res==" + res);
        return res;
    }

    @Override
    /**
     * 涨停第二波 20厘米的
     */
    public String checkAndDateRangeBollLimit(LocalDate startDate, LocalDate endDate) {
        String res = "";
        String res20cm = "涨停扩张:";

        Map<String, String> codestockMap = new HashMap<>();
//        String flowstock = iwencaiConfig.getFocus();
        List<StockLimitUp> now10limit  = iwencaiConfig.getLimitstocks10d();
//        now10Drecords.addAll(iwencaiConfig.get());

//            Map<String, StockQueryRecord> stockMap = new HashMap<>();

        // 判空处理，避免NullPointerException
        if (now10limit != null) {
            // 遍历列表，将stockCode作为键，对象作为值存入Map
            for (StockLimitUp record : now10limit) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
//                        flowstock+=","+record.getStockCode();
                    codestockMap.put(record.getStockCode(),record.getStockCode());
//                        stockMap.put(record.getStockCode(), record);
                }
            }
        }

        List<StockQueryRecord> now10Drecords = iwencaiConfig.getNow10Drecords();
        Map<String, String> stockMap = new HashMap<>();
        if (now10Drecords != null && !now10Drecords.isEmpty()) {
            // 分割并过滤空字符串
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(),record.getStockCode());
                }
            }
        }
//        System.out.println("stockMap=="+stockMap);

        Set<String> keySet = codestockMap.keySet();
        for (String key : keySet) {
//        for (int i = 0; i <codestockMap.size() ; i++) {

            String code=key;
//            System.out.println("code==="+code);
            //List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
            List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAsc(endDate, endDate,code);

            for (int j = 0; j < daysAllStock.size(); j++) {
                Stock codestock = daysAllStock.get(j);



                //            if (kdjStocks==null || !kdjStocks.containsKey(codestock.getCode())) { //是否在KDJ内 TODO
                //                continue; // 空列表直接返回
                //            }

                if (stockMap==null || !stockMap.containsKey(codestock.getCode())) { //是否在10天查询内
                    continue; // 空列表直接返回
                }

                List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(codestock.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));
                //            System.out.println("Stock allStock=="+allStock.size()+"="+allStock);
                if (allStock.isEmpty()) {
                    return res; // 空列表直接返回
                }
                // 按创建时间降序排序，确保最新数据在前面
                allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

                int index = 20;
                if (allStock != null && allStock.size() < index) {
                    index = allStock.size();
                }

                boolean flag = true;
                if (allStock != null && allStock.size() >= index  && index>2) {
                    Stock stock0 = allStock.get(0); // 今天的数据
                    Stock stock1 = allStock.get(1); // 今天的数据
                    Stock stock2 = allStock.get(2); // 今天的数据
                    //                Stock stock3 = allStock.get(3); // 今天的数据
                    //                Stock stock4 = allStock.get(4); // 今天的数据

                    List<Stock> stockList = new ArrayList<>();
                    stockList.add(stock0);
                    stockList.add(stock1);
                    stockList.add(stock2);
                    //                stockList.add(stock3);
                    //                stockList.add(stock4);

                    double profitPercent = 0.0;
                    double highCPercent = 0.0;//最高价与现价征服

                    //                Set<String> stockset = DataUtil.processSecondString(stock0.getConcept());

                    // 检查交集
                    //                boolean hasIntersection = DataUtil.checkIntersection(Reviewset, stockset);

                    if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                        profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                    }

                    highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                    //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
                    //                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                    boolean mainstockFlag = allMatchConditionStock(stock0);
                    boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                    boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
                    //                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    //                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
                    //                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                    if (stock0 != null
//                            && mainstockFlag //主力流入或散户流出多
//                            && mainFlag //当天主力进入十万
                            //                        && hasIntersection //有关联概念
                            && mainCost5DFlag //5天主力成本都是再增加
                            //                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                            && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                            && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率
//                            && stock0.getChangePercent()<=4.5 && stock0.getChangePercent()>=0.1 //涨幅在0.5~4.5%之间
//                            && highClostFlag //大于开盘价
//                            && Math.abs(highCPercent)<=3 //振幅3%以内
//                            && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升
                    ) {
                        //60分boll扩张
                        List<Stock> threeDaysData = new ArrayList<>();
                        List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(codestock.getCode(),DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
                        if (s60bolls!=null){
                            List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
                            //                        System.out.println("s60bollsToStock=="+s60bollsToStock);
                            if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
                                threeDaysData.add(s60bollsToStock.get(0));
                                threeDaysData.add(s60bollsToStock.get(1));
                                threeDaysData.add(s60bollsToStock.get(2));

                                BollingerBandCalculator calculator = new BollingerBandCalculator();
//                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.01);
                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayLimitStatus(threeDaysData, 0.01);
                                if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                    flag = true;
                                } else {
                                    flag = false;
                                }
                            }else{
                                flag = false;
                            }
                        }else{
                            flag = false;
                        }

                        //日开口
//                        if (flag) {
//                            BollingerBandCalculator calculator = new BollingerBandCalculator();
//                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(stockList, 0.01);
//                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
//                                flag = true;
//                            } else {
//                                flag = false;
//                            }
//                        }
//                        log.info("checkAndDateRangeBollLimit threeDaysData=="+threeDaysData+"==="+codestock.getCode());
                    } else {
                        flag = false;
                    }

                    if (flag) {
                        res += codestock.getCode() + ",";
                        if (codestock.getCode().startsWith("3")  || codestock.getCode().startsWith("68") ) {
                            res20cm += codestock.getCode() + ",";
//                            continue; // 空列表直接返回
                        }
                    }
                }
            }
        }
        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("Boll扩张-涨停");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//
        iwencaiConfig.setFocusBoll(iwencaiConfig.getFocusBoll()+"<br>"+res20cm);
        log.info("checkAndDateRangeBollLimit===res==" + res);
        return res;
    }

    @Override
    /**
     * 涨停或者涨幅大于1%  15天内横盘的。。
     */
    public String checkAndDateRangeBollLimitOpen() {
        String res = "";

        LocalDate startDate = LocalDate.now().minusDays(20);
        LocalDate endDate = DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT), 1).toLocalDate();//1天前的数据
        List<Stock> limitOrderByCodeAsc = stockRepository.findByTradeDateBetweenLimitOrderByCodeAsc(LocalDate.now(), LocalDate.now());
        System.out.println("checkAndDateRangeBollLimitOpen=="+limitOrderByCodeAsc.size()+"===");

        List<StockQueryRecord> now10Drecords = iwencaiConfig.getNow10Drecords();

        Map<String, String> stockMap = new HashMap<>();
        if (now10Drecords != null && !now10Drecords.isEmpty()) {

//            if (iwencaiConfig.getNowTodayDrecords()!=null){ //当天查询出来
//                now10Drecords.addAll(iwencaiConfig.getNowTodayDrecords());
//            }
            // 分割并过滤空字符串
            for (StockQueryRecord record : now10Drecords) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getStockCode() != null) {
                    stockMap.put(record.getStockCode(),record.getStockCode());
                }
            }
        }

        String hpture="";

        for (int i = 0; i <limitOrderByCodeAsc.size() ; i++) {
            Stock scode = limitOrderByCodeAsc.get(i);

            if (stockMap==null || !stockMap.containsKey(scode.getCode())) { //是否在10天查询内
                continue; // 空列表直接返回
            }
                List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(scode.getCode(), LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));

//                System.out.println("Stock allStock=="+scode.getCode()+"====="+allStock.size()+"=");
                if (allStock.isEmpty()) {
                    return res; // 空列表直接返回
                }
                // 按创建时间降序排序，确保最新数据在前面
                allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

                int index = 20;
                if (allStock != null && allStock.size() < index) {
                    index = allStock.size();
                }

//            System.out.println("index==="+index);
                boolean flag = true;
                if (allStock != null && allStock.size() >= index && index>2) {
                    Stock stock0 = allStock.get(0); // 今天的数据
                    Stock stock1 = allStock.get(1); // 今天的数据
                    Stock stock2 = allStock.get(2); // 今天的数据
                    //                Stock stock3 = allStock.get(3); // 今天的数据
                    //                Stock stock4 = allStock.get(4); // 今天的数据

                    List<Stock> stockList = new ArrayList<>();
                    stockList.add(scode);
                    stockList.add(stock0);
                    stockList.add(stock1);
                    stockList.add(stock2);
                    //                stockList.add(stock3);
                    //                stockList.add(stock4);

                    double profitPercent = 0.0;
                    double highCPercent = 0.0;//最高价与现价征服


                    if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                        profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                    }

//                    StockSidewaysDetector.SidewaysAnalysisResult result = StockSidewaysDetector.detectSideways(allStock, startDate, endDate);
//                    System.out.println(result);
//                    if (result==null  || !result.isSideways()){
//                        continue;
//                    }

                    StockSidewaysDetector.PriceRangeAnalysisResult priceresult = StockSidewaysDetector.isHighLowRangeWithin20Percent(allStock);
//                    System.out.println(priceresult);
                    if (priceresult==null  || !priceresult.isWithin20Percent()){
                        continue;
                    }
                    hpture+=scode.getCode()+",";
                    highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                    //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
                    //                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                    boolean mainstockFlag = allMatchConditionStock(stock0);
                    boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                    boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
                    //                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    //                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
                    //                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                    if (stock0 != null
                            && mainstockFlag //主力流入或散户流出多
//                            && mainFlag //当天主力进入十万
                            //                        && hasIntersection //有关联概念
                            //                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                            && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                            && scode.getTurnoverRate() > 2 && scode.getTurnoverRate() < 20 //换手率
//                            && stock0.getChangePercent()<=5.5 && stock0.getChangePercent()>=0.1 //涨幅在0.5~4.5%之间
                            && highClostFlag //大于开盘价
//                            && Math.abs(highCPercent)<=3 //振幅3%以内

                            && mainCost5DFlag //5天主力成本都是再增加 TODO
                            && scode.getRankPosition()<=scode.getOldRankPosition() //热度上升 TODO
                    ) {
                        //60分boll扩张
                        List<Stock> threeDaysData = new ArrayList<>();
                        List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(scode.getCode(),DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
                        if (s60bolls!=null){
                            List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
                            //                        System.out.println("s60bollsToStock=="+s60bollsToStock);
                            if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
                                threeDaysData.add(s60bollsToStock.get(0));
                                threeDaysData.add(s60bollsToStock.get(1));
                                threeDaysData.add(s60bollsToStock.get(2));

                                BollingerBandCalculator calculator = new BollingerBandCalculator();
//                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.01);
                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayLimitStatus(threeDaysData, 0.01);
                                if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                    flag = true;
                                } else {
                                    flag = false;
                                }
                            }else{
                                flag = false;
                            }
                        }else{
                            flag = false;
                        }

                        //日开口
                        if (flag) {
                            BollingerBandCalculator calculator = new BollingerBandCalculator();
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(stockList, 0.01);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }
//                        log.info("checkAndDateRangeBollLimit threeDaysData=="+threeDaysData+"==="+codestock.getCode());
                    } else {
                        flag = false;
                    }

                    if (flag) {
                        res += scode.getCode() + ",";
                    }
                }
//            }
        }
        SysInfo sysInfo = new SysInfo();
        sysInfo.setSysKey("Boll扩张-涨停开板");
        sysInfo.setSysValue(res);
        sysInfo.setTypes(1);
        sysInfo.setCreateTime(LocalDateTime.now());
        upsertSysInfo(sysInfo);//
        iwencaiConfig.setFocusBoll(iwencaiConfig.getFocusBoll()+"<br>涨停开板:"+res);
        log.info("checkAndDateRangeBollLimitOpen===res==" + res);
//        log.info("hpture===hpture==" + hpture);
        return res;
    }
    @Override
    /**
     * 复盘扩张2天扩张的。。
     */
    public String checkAndDateRangeBoll3D() {
        String res = "复盘扩张:";

        LocalDate startDate = LocalDate.now().minusDays(20);
//        LocalDate endDate = DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT), 1).toLocalDate();//1天前的数据
        List<ReviewInfo> reviewInfos = reviewInfoRepository.findByReviewTimeBetween(startDate, LocalDate.now().minusDays(1));
        System.out.println("checkAndDateRangeBoll3D=="+reviewInfos.size()+"===");

//        List<StockQueryRecord> now10Drecords = iwencaiConfig.getNow10Drecords();

        Map<String, String> stockMap = new HashMap<>();
        if (reviewInfos != null && !reviewInfos.isEmpty()) {
            for (ReviewInfo record : reviewInfos) {
                // 避免处理null对象和null的stockCode
                if (record != null && record.getReviewStock() != null) {
//                    stockMap.put(record.getStockCode(),record.getStockCode());

                    // 将以逗号分隔的stockCode字符串分割成数组
                    String[] stockCodes = record.getReviewStock().split(",");

                    // 遍历分割后的每个stockCode
                    for (String stockCode : stockCodes) {
                        // 去除前后空格并检查是否为空
                        String trimmedCode = stockCode.trim();
                        if (!trimmedCode.isEmpty()) {
                            stockMap.put(trimmedCode, trimmedCode);
                        }
                    }
                }
            }
        }

//        System.out.println("checkAndDateRangeBoll3D stockMap=="+stockMap);
//        String hpture="";

        Set<String> keySet = stockMap.keySet();
        for (String code : keySet) {
//        for (int i = 0; i <limitOrderByCodeAsc.size() ; i++) {
//            Stock scode = limitOrderByCodeAsc.get(i);

//            if (stockMap==null || !stockMap.containsKey(scode.getCode())) { //是否在10天查询内
//                continue; // 空列表直接返回
//            }
                List<Stock> allStock = stockRepository.findByCodeAndCreatedAtBetweenOrderByTradeDateAsc(code, LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(LocalDate.now(), LocalTime.MAX));

//                System.out.println("Stock allStock=="+scode.getCode()+"====="+allStock.size()+"=");
                if (allStock.isEmpty()) {
                    return res; // 空列表直接返回
                }
                // 按创建时间降序排序，确保最新数据在前面
                allStock.sort(Comparator.comparing(Stock::getCreatedAt).reversed());

                int index = 20;
                if (allStock != null && allStock.size() < index) {
                    index = allStock.size();
                }

//            System.out.println("index==="+index);
                boolean flag = true;
                if (allStock != null && allStock.size() >= index && index>2) {
                    Stock stock0 = allStock.get(0); // 今天的数据
                    Stock stock1 = allStock.get(1); // 今天的数据
                    Stock stock2 = allStock.get(2); // 今天的数据
                    //                Stock stock3 = allStock.get(3); // 今天的数据
                    //                Stock stock4 = allStock.get(4); // 今天的数据

                    List<Stock> stockList = new ArrayList<>();
                    stockList.add(stock0);
                    stockList.add(stock1);
                    stockList.add(stock2);
                    //                stockList.add(stock3);
                    //                stockList.add(stock4);

                    double profitPercent = 0.0;
                    double highCPercent = 0.0;//最高价与现价征服


                    if (stock0.getMainCost() != null && stock0.getMainCost() != 0) {
                        profitPercent = ((stock0.getClose() - stock0.getMainCost()) / stock0.getMainCost());
                    }

//                    StockSidewaysDetector.SidewaysAnalysisResult result = StockSidewaysDetector.detectSideways(allStock, startDate, endDate);
//                    System.out.println(result);
//                    if (result==null  || !result.isSideways()){
//                        continue;
//                    }

                    StockSidewaysDetector.PriceRangeAnalysisResult priceresult = StockSidewaysDetector.isHighLowRangeWithin20Percent(allStock);
//                    System.out.println(priceresult);
                    if (priceresult==null  || !priceresult.isWithin20Percent()){
                        continue;
                    }
//                    hpture+=scode.getCode()+",";
                    highCPercent=((stock0.getHigh() - stock0.getClose()) / stock0.getClose());
                    //换手>3% rsi6>30 并且短期上涨 利润少于20% 主力进散户出 TODO
                    //                stock0.getCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(stock0.getCapitalFlow())>0 &&
                    boolean mainstockFlag = allMatchConditionStock(stock0);
                    boolean mainFlag = (stock0.getMainCapitalFlow()!=null && stock0.getMainCapitalFlow().compareTo(new BigDecimal(10*10000))>0);
                    boolean highClostFlag = (stock0.getClose()>=stock0.getOpen());
                    //                boolean main5DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    //                boolean main10DFlag = (calculateTotalMainCapitalFlow(stockList,0).compareTo(new BigDecimal(1000*10000))>0); //5天主力流入
                    boolean mainCost5DFlag = allMatchCondition(stockList); //5天主力成本都是再增加
                    //                boolean capitalFlag = (stock0.getCapitalFlow()!=null && stock0.getCapitalFlow().compareTo(new BigDecimal(1000*10000))>0);
                    if (stock0 != null
//                            && mainstockFlag //主力流入或散户流出多
//                            && mainFlag //当天主力进入十万
                            //                        && hasIntersection //有关联概念
                            //                        && stock0.getTurnoverRate() <= 30 && stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                            && profitPercent <= 0.16 && profitPercent >= 0.01 //获利 1%~20%之间
                            && stock0.getTurnoverRate() > 2 && stock0.getTurnoverRate() < 20 //换手率
//                            && stock0.getChangePercent()<=5.5 && stock0.getChangePercent()>=0.1 //涨幅在0.5~4.5%之间
//                            && highClostFlag //大于开盘价
//                            && Math.abs(highCPercent)<=3 //振幅3%以内

                            && mainCost5DFlag //5天主力成本都是再增加 TODO
                            && stock0.getRankPosition()<=stock0.getOldRankPosition() //热度上升 TODO
                    ) {
                        //60分boll扩张
//                        List<Stock> threeDaysData = new ArrayList<>();
//                        List<Stock60Boll> s60bolls = stock60BollRepository.findRecentDataByCode(scode.getCode(),DateUtils.minusDaysExcludeWeekendAndYesterday(LocalDateTime.now(),2).toLocalDate());
//                        if (s60bolls!=null){
//                            List<Stock>  s60bollsToStock = s60bollsToStock(s60bolls);
//                            //                        System.out.println("s60bollsToStock=="+s60bollsToStock);
//                            if (s60bollsToStock!=null && s60bollsToStock.size()>=2){
//                                threeDaysData.add(s60bollsToStock.get(0));
//                                threeDaysData.add(s60bollsToStock.get(1));
//                                threeDaysData.add(s60bollsToStock.get(2));
//
//                                BollingerBandCalculator calculator = new BollingerBandCalculator();
////                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayStatus(threeDaysData, 0.01);
//                                BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judgeTodayLimitStatus(threeDaysData, 0.01);
//                                if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
//                                    flag = true;
//                                } else {
//                                    flag = false;
//                                }
//                            }else{
//                                flag = false;
//                            }
//                        }else{
//                            flag = false;
//                        }

                        //日开口
                        if (flag) {
                            BollingerBandCalculator calculator = new BollingerBandCalculator();
                            BollingerBandCalculator.BollingerAnalysisResult todayResult = calculator.judge3DStatus(stockList, 0.01);
                            if (todayResult.getBandStatus().getKey().equals("EXPANDING")) {
                                flag = true;
                            } else {
                                flag = false;
                            }
                        }
//                        log.info("checkAndDateRangeBollLimit threeDaysData=="+threeDaysData+"==="+codestock.getCode());
                    } else {
                        flag = false;
                    }

                    if (flag) {
                        res += code + ",";
                    }
                }
//            }
        }
//        SysInfo sysInfo = new SysInfo();
//        sysInfo.setSysKey("Boll扩张-涨停开板");
//        sysInfo.setSysValue(res);
//        sysInfo.setTypes(1);
//        sysInfo.setCreateTime(LocalDateTime.now());
//        upsertSysInfo(sysInfo);//
        iwencaiConfig.setFocusBoll(iwencaiConfig.getFocusBoll()+"<br>"+res);
        log.info("checkAndDateRangeBoll3D===res==" + res);
//        log.info("hpture===hpture==" + hpture);
        return res;
    }

    /**
     * 60分钟获取最近的boll值
     * @param s60bolls
     * @return
     */
    public List<Stock> s60bollsToStock(List<Stock60Boll> s60bolls){
        if (s60bolls == null || s60bolls.isEmpty()) {
            log.warn("s60bolls list is null or empty");
            return new ArrayList<>();
        }

        // 转换并按时间降序排列
        return s60bolls.stream()
                .filter(Objects::nonNull)
                .flatMap(this::convertSingleStock60Boll)
                .sorted(Comparator.comparing(Stock::getCreatedAt).reversed()) // 按时间降序
                .collect(Collectors.toList());
    }

    /**
     * 转换单个Stock60Boll为多个Stock对象（每个时间点一个）
     */
    private java.util.stream.Stream<Stock> convertSingleStock60Boll(Stock60Boll stock60Boll) {
        List<Stock> stocks = new ArrayList<>();

        // 处理各个时间点的boll数据
        addStockFromBollField(stock60Boll, stock60Boll.getBoll9(), LocalTime.of(9, 30), stocks);
        addStockFromBollField(stock60Boll, stock60Boll.getBoll10(), LocalTime.of(10, 30), stocks);
        addStockFromBollField(stock60Boll, stock60Boll.getBoll11(), LocalTime.of(11, 30), stocks);
        addStockFromBollField(stock60Boll, stock60Boll.getBoll13(), LocalTime.of(13, 0), stocks);
        addStockFromBollField(stock60Boll, stock60Boll.getBoll14(), LocalTime.of(14, 0), stocks);
        addStockFromBollField(stock60Boll, stock60Boll.getBoll15(), LocalTime.of(15, 0), stocks);

        return stocks.stream();
    }

    /**
     * 从boll字段创建Stock对象
     */
    private void addStockFromBollField(Stock60Boll stock60Boll, String bollValue,
                                       LocalTime time, List<Stock> stocks) {

        if (bollValue == null || bollValue.trim().isEmpty()) {
            return;
        }

        try {
            String[] values = bollValue.split("#");

            if (values.length < 4) {
                log.error("Invalid boll format: {} for stock {}", bollValue, stock60Boll.getCode());
                return;
            }

            LocalDate tradeDate = stock60Boll.getTradeDate();
            LocalDateTime createdAt = LocalDateTime.of(tradeDate, time);
            Stock stock = new Stock();
            stock.setCode(stock60Boll.getCode());
            stock.setName(stock60Boll.getName());
            stock.setCreatedAt(createdAt);
            stock.setBollMid(parseDouble(values[0]));
            stock.setBollLower(parseDouble(values[1]));
            stock.setBollUpper(parseDouble(values[2]));
            stock.setClose(parseDouble(values[3]));
            stocks.add(stock);

        } catch (Exception e) {
            log.error("Error converting boll value: {}", bollValue, e);
        }
    }

    /**
     * 安全解析double值
     */
    private Double parseDouble(String value) {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }

        try {
            return Double.parseDouble(value);
        } catch (NumberFormatException e) {
            log.error("Invalid number format: {}", value, e);
            return null;
        }
    }


    /**
     * 当天前100名，热度上升
     * @return
     */
    @Override
    public String checkAndDateRangeRank(LocalDate startDate, LocalDate endDate, Map<String, Stock> kdjStocks) {
        List<Stock> daysAllStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(endDate, endDate);
        log.info(endDate + "=checkAndDateRangeRank=" + daysAllStock.size());
        String res = "";
        if (daysAllStock != null) {
            for (int j = 0; j < daysAllStock.size(); j++) {
                Stock stock0 = daysAllStock.get(j);
                if (kdjStocks == null || !kdjStocks.containsKey(stock0.getCode())) { //是否在KDJ内 TODO
                    continue; // 空列表直接返回
                }

                //&& stock0.getRsi6() > 30 && stock0.getRsi6() >= stock0.getRsi12() && stock0.getRsi12() >= stock0.getRsi24()
                int index = 50;
                if (stock0 != null && stock0.getRankPosition() <= index
//                        && stock0.getOldRankPosition() >= stock0.getRankPosition()
                ) {
                    res += stock0.getCode() + ",";
                }
            }

//            if (res != null && !res.trim().isEmpty()) {
            SysInfo sysInfo = new SysInfo();
            sysInfo.setSysKey("当天前100升");
            sysInfo.setSysValue(res);
            sysInfo.setTypes(1);
            sysInfo.setCreateTime(LocalDateTime.now());
            upsertSysInfo(sysInfo);//
//            }
            log.info("checkAndDateRangeRank res==" + res);
        }
//        System.out.println(result);
//        result.sort(Comparator.comparing(StockSummaryDTO::getCreatedAt).reversed());
        return res;
    }


    /**
     * 方法1：将5个Stock加入列表，并计算getMainCapitalFlow的总和
     * @return 所有Stock的mainCapitalFlow总和（BigDecimal）
     */
    public BigDecimal calculateTotalMainCapitalFlow(List<Stock> stockList,int leng) {
        // 计算总和（处理可能的null值）
        if (leng==0 || leng>stockList.size()){
            leng = stockList.size();
        }
        BigDecimal totalFlow = BigDecimal.ZERO;
        for (int i = 0; i <leng; i++) {
            Stock stock = stockList.get(i);
            BigDecimal flow = stock.getMainCapitalFlow();
            if (flow != null) {
                totalFlow = totalFlow.add(flow);
            }
        }
//        for (Stock stock : stockList) {
//            BigDecimal flow = stock.getMainCapitalFlow();
//            if (flow != null) {
//                totalFlow = totalFlow.add(flow);
//            }
//        }
        return totalFlow;
    }

    /**
     * 判断列表中所有连续的Stock对是否都满足条件：
     * 当前Stock的mainCost为0 或者 前一个Stock的mainCost大于等于当前Stock的mainCost
     * @param stockList 股票列表
     * @return 所有连续对都满足条件返回true，否则返回false
     */
    public boolean allMatchCondition(List<Stock> stockList) {
        // 遍历所有连续的Stock对
        for (int i = 1; i < stockList.size(); i++) {
            Stock prevStock = stockList.get(i - 1);
            Stock currStock = stockList.get(i);

            Double prevCost = prevStock.getMainCost();
            Double currCost = currStock.getMainCost();

            // 若任一值为null，视为不满足条件
            if (prevCost == null || currCost == null) {
                return false;
            }
            // 条件1：当前mainCost为0
            boolean condition1 = prevCost == 0;
            // 条件2：前一个mainCost >= 当前mainCost
            boolean condition2 = prevCost>= currCost;

            // 只要有一对不满足条件，立即返回false
            if (!(condition1 || condition2)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断列表中所有连续的Stock对是否都满足条件：
     * 当前Stock的mainCost为0 或者 前一个Stock的mainCost大于等于当前Stock的mainCost
     * @param stock 股票列表
     * @return 所有连续对都满足条件返回true，否则返回false
     */
    public boolean allMatchConditionStock(Stock stock) {
        // 获取所需的资金流数据
        BigDecimal capitalFlow = stock.getCapitalFlow();
        BigDecimal mainCapitalFlow = stock.getMainCapitalFlow();

        // 若任一值为null，无法进行比较，视为不满足条件
        if (capitalFlow == null || mainCapitalFlow == null) {
            return false;
        }

        // 计算capitalFlow与mainCapitalFlow的差值（其他资金流）
        BigDecimal otherCapitalFlow = capitalFlow.subtract(mainCapitalFlow);

        // 条件1：mainCapitalFlow大于0（表示存在正向主力资金流）
        boolean condition1 = mainCapitalFlow.compareTo(BigDecimal.ZERO) > 0;

        // 条件2：mainCapitalFlow大于其他资金流（表示主力资金流占比更高）
        boolean condition2 = mainCapitalFlow.compareTo(otherCapitalFlow) > 0;

        // 满足两个条件中的任意一个即返回true
        return condition1 || condition2;
    }



    /**
     * 计算指定天数范围内的主力资金流向总和
     * @param stocks       股票列表（已按时间降序排序）
     * @param baseTime     基准时间（最新记录的创建时间）
     * @param startDaysAgo 起始天数（几天前）
     * @param endDaysAgo   结束天数（几天前）
     * @return 总和（null视为0）
     */
    private BigDecimal calculateSumMainCapitalInRange(List<Stock> stocks, LocalDateTime baseTime, int startDaysAgo, int endDaysAgo) {
//        LocalDateTime startTime = baseTime.minusDays(endDaysAgo);
//        LocalDateTime endTime = baseTime.minusDays(startDaysAgo);

        LocalDateTime startTime = DateUtils.minusDaysExcludeWeekendAndYesterday(baseTime, endDaysAgo);

        return stocks.stream()
                .filter(stock -> {
                    LocalDateTime createdAt = stock.getCreatedAt();
                    // 过滤出在指定时间范围内的记录
                    return createdAt != null &&
                            createdAt.isAfter(startTime) &&
                            createdAt.isBefore(baseTime);
                })
                .map(stock -> stock.getMainCapitalFlow() != null ? stock.getMainCapitalFlow() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 计算指定天数范围内的资金流向总和
     *
     * @param stocks       股票列表（已按时间降序排序）
     * @param baseTime     基准时间（最新记录的创建时间）
     * @param startDaysAgo 起始天数（几天前）
     * @param endDaysAgo   结束天数（几天前）
     * @return 总和（null视为0）
     */
    private BigDecimal calculateSumCapitalInRange(List<Stock> stocks, LocalDateTime baseTime, int startDaysAgo, int endDaysAgo) {
//        LocalDateTime startTime = baseTime.minusDays(endDaysAgo);
//        LocalDateTime endTime = baseTime.minusDays(startDaysAgo);

        LocalDateTime startTime = DateUtils.minusDaysExcludeWeekendAndYesterday(baseTime, endDaysAgo);
//        LocalDateTime endTime = service.minusDaysExcludeWeekendAndYesterday(baseTime, startDaysAgo);

        return stocks.stream()
                .filter(stock -> {
                    LocalDateTime createdAt = stock.getCreatedAt();
                    // 过滤出在指定时间范围内的记录
                    return createdAt != null &&
                            createdAt.isAfter(startTime) &&
                            createdAt.isBefore(baseTime);
                })
                .map(stock -> stock.getCapitalFlow() != null ? stock.getCapitalFlow() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }


    @Override
    public List<StockSummaryDTO> getStockSummaryData(LocalDate today) {
//        List<Stock> allStock = stockRepository.findByCreatedAtBetweenOrderByRankPositionAsc(LocalDateTime.of(today, LocalTime.MIDNIGHT), LocalDateTime.of(today, LocalTime.MAX));
        List<Stock> allStock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(today, today);

//        log.info("allStock=="+LocalDateTime.of(today, LocalTime.MIDNIGHT).toLocalDate()+"==="+ LocalDateTime.of(today, LocalTime.MAX));
        if (allStock == null || allStock.size() <= 0) {
            return new ArrayList<>();
        }
        Map<String, StockSummaryDTO> resultMap = new LinkedHashMap<>();
        LocalDateTime now = LocalDateTime.of(today, LocalTime.MIDNIGHT);
        LocalDateTime latestTime = LocalDateTime.of(now.toLocalDate(), LocalTime.MAX);

        LocalDateTime startDate1d = DateUtils.minusDaysExcludeWeekendAndYesterday(now, 1);//.toLocalDate();//today.minusDays(1);
//        List<CapitalFlowSummary> listday1 = stockRepository.sumCapitalFlowsByDateRange(now.toLocalDate(), today);
//
//        LocalDateTime startDate2d = service.minusDaysExcludeWeekendAndYesterday(now, 1);//.toLocalDate();//today.minusDays(1);
//        List<CapitalFlowSummary> listday2 = stockRepository.sumCapitalFlowsByDateRange(startDate2d.toLocalDate(), today);
//
//        LocalDateTime startDate3d = service.minusDaysExcludeWeekendAndYesterday(now, 2);//.toLocalDate();//today.minusDays(1);
//        List<CapitalFlowSummary> listday3 = stockRepository.sumCapitalFlowsByDateRange(startDate3d.toLocalDate(), today);
//
//        LocalDateTime startDate5d = service.minusDaysExcludeWeekendAndYesterday(now, 4);//.toLocalDate();//today.minusDays(1);
//        List<CapitalFlowSummary> listday5 = stockRepository.sumCapitalFlowsByDateRange(startDate5d.toLocalDate(), today);
//
//        LocalDateTime startDate6d = service.minusDaysExcludeWeekendAndYesterday(now, 5);//.toLocalDate();//today.minusDays(1);
//        List<CapitalFlowSummary> listday6 = stockRepository.sumCapitalFlowsByDateRange(startDate6d.toLocalDate(), today);

        LocalDateTime startDate7d = DateUtils.minusDaysExcludeWeekendAndYesterday(now, 7);//.toLocalDate();//today.minusDays(1);
//        List<CapitalFlowSummary> listday7 = stockRepository.sumCapitalFlowsByDateRange(startDate7d.toLocalDate(), today);

        HashMap<String, List<Stock>> allMap = new HashMap<>();
        List<Stock> all7Stock = iwencaiConfig.getAll7Stock();
        if (all7Stock == null || all7Stock.size() < 1 || !today.isEqual(LocalDate.now())) {
            log.info("all7Stock  search");
            all7Stock = stockRepository.findByTradeDateBetweenOrderByCodeAscTradeDateAsc(startDate7d.toLocalDate(), startDate1d.toLocalDate());
        }
        all7Stock.addAll(allStock);

        for (int i = 0; i < all7Stock.size(); i++) {
            Stock stock = all7Stock.get(i);
            if (stock != null) {
                String code = stock.getCode();
                // 检查map中是否已存在该代码的列表
                if (allMap.containsKey(code)) {
                    // 存在则直接添加到现有列表
                    allMap.get(code).add(stock);
                } else {
                    // 不存在则创建新列表并放入map
                    List<Stock> stockList = new ArrayList<>();
                    stockList.add(stock);
                    allMap.put(code, stockList);
                }
            }
        }

        if (allStock != null) {
            for (Stock stock : allStock) {
                StockSummaryDTO summary = new StockSummaryDTO(stock.getCode());
                summary.setCurrentRankPosition(stock.getRankPosition());
                summary.setCurrentOldRankPosition(stock.getOldRankPosition());
                summary.setPrice(stock.getClose());
                summary.setChangePercent(stock.getChangePercent() != null ? stock.getChangePercent() : 0.0d);
                summary.setName(stock.getName());
                summary.setNamepy(PinyinUtils.getWordInitials(stock.getName()));
                summary.setConcept(stock.getConcept());
                summary.setMarketValue(stock.getMarketValue());
                summary.setMainCost(stock.getMainCost());
                summary.setTurnoverRate(stock.getTurnoverRate());

                summary.setVolume(stock.getVolume());
                summary.setRsi6(stock.getRsi6());
                summary.setRsi12(stock.getRsi12());
                summary.setRsi24(stock.getRsi24());

                List<Stock> stockList = allMap.get(stock.getCode());
                if (stockList != null && !stockList.isEmpty()) {
                    // 方法1：使用Collections.sort() + 匿名内部类
                    Collections.sort(stockList, new Comparator<Stock>() {
                        @Override
                        public int compare(Stock s1, Stock s2) {
                            // 按tradeDate降序排列（后发生的日期排在前面）
                            return s2.getCreatedAt().compareTo(s1.getCreatedAt());
                        }
                    });
                }

//                summary.setMainCapitalFlow1d(calculateSumMainCapitalInRange(stockList, latestTime, 0, 0));
//                summary.setCapitalFlow1d(calculateSumCapitalInRange(stockList, latestTime, 0, 0));
                summary.setMainCapitalFlow1d(stock.getMainCapitalFlow());
                summary.setCapitalFlow1d(stock.getCapitalFlow());

                // 1天前的7天（1-8天前）
                summary.setMainCapitalFlow2d(calculateSumMainCapitalInRange(stockList, latestTime, 0, 1));
                summary.setCapitalFlow2d(calculateSumCapitalInRange(stockList, latestTime, 0, 1));

                // 2天前的7天（2-9天前）
                summary.setMainCapitalFlow3d(calculateSumMainCapitalInRange(stockList, latestTime, 0, 2));
                summary.setCapitalFlow3d(calculateSumCapitalInRange(stockList, latestTime, 0, 2));

                // 3天前的7天（3-10天前）
                summary.setMainCapitalFlow5d(calculateSumMainCapitalInRange(stockList, latestTime, 0, 4));
                summary.setCapitalFlow5d(calculateSumCapitalInRange(stockList, latestTime, 0, 4));

                // 4天前的7天（4-11天前）
                summary.setMainCapitalFlow6d(calculateSumMainCapitalInRange(stockList, latestTime, 0, 5));
                summary.setCapitalFlow6d(calculateSumCapitalInRange(stockList, latestTime, 0, 5));

                // 5天前的7天（5-12天前）
                summary.setMainCapitalFlow7d(calculateSumMainCapitalInRange(stockList, latestTime, 0, 6));
                summary.setCapitalFlow7d(calculateSumCapitalInRange(stockList, latestTime, 0, 6));

                resultMap.put(stock.getCode(), summary);
            }
        }

//        for (int i = 0; i < listday1.size(); i++) {
//            CapitalFlowSummary capitalFlowSummary = listday1.get(i);
//            if (capitalFlowSummary != null) {
//                StockSummaryDTO summary = resultMap.get(capitalFlowSummary.getCode());
//                if (summary != null) {
//                    summary.setCapitalFlow1d(capitalFlowSummary.getSumCapitalFlow());
//                    summary.setMainCapitalFlow1d(capitalFlowSummary.getSumMainCapitalFlow());
//                    resultMap.put(capitalFlowSummary.getCode(), summary);
//                }
//            }
//        }
//
//        for (int i = 0; i < listday2.size(); i++) {
//            CapitalFlowSummary capitalFlowSummary = listday2.get(i);
//            if (capitalFlowSummary != null) {
//                StockSummaryDTO summary = resultMap.get(capitalFlowSummary.getCode());
//                if (summary != null) {
//                    summary.setCapitalFlow2d(capitalFlowSummary.getSumCapitalFlow());
//                    summary.setMainCapitalFlow2d(capitalFlowSummary.getSumMainCapitalFlow());
//                    resultMap.put(capitalFlowSummary.getCode(), summary);
//                }
//            }
//        }
//
//
//        for (int i = 0; i < listday3.size(); i++) {
//            CapitalFlowSummary capitalFlowSummary = listday3.get(i);
//            if (capitalFlowSummary != null) {
//                StockSummaryDTO summary = resultMap.get(capitalFlowSummary.getCode());
//                if (summary != null) {
//                    summary.setCapitalFlow3d(capitalFlowSummary.getSumCapitalFlow());
//                    summary.setMainCapitalFlow3d(capitalFlowSummary.getSumMainCapitalFlow());
//                    resultMap.put(capitalFlowSummary.getCode(), summary);
//                }
//            }
//        }
//
//
//        for (int i = 0; i < listday5.size(); i++) {
//            CapitalFlowSummary capitalFlowSummary = listday5.get(i);
//            if (capitalFlowSummary != null) {
//                StockSummaryDTO summary = resultMap.get(capitalFlowSummary.getCode());
//                if (summary != null) {
//                    summary.setCapitalFlow5d(capitalFlowSummary.getSumCapitalFlow());
//                    summary.setMainCapitalFlow5d(capitalFlowSummary.getSumMainCapitalFlow());
//                    resultMap.put(capitalFlowSummary.getCode(), summary);
//                }
//            }
//        }
//
//        for (int i = 0; i < listday6.size(); i++) {
//            CapitalFlowSummary capitalFlowSummary = listday6.get(i);
//            if (capitalFlowSummary != null) {
//                StockSummaryDTO summary = resultMap.get(capitalFlowSummary.getCode());
//                if (summary != null) {
//                    summary.setCapitalFlow6d(capitalFlowSummary.getSumCapitalFlow());
//                    summary.setMainCapitalFlow6d(capitalFlowSummary.getSumMainCapitalFlow());
//                    resultMap.put(capitalFlowSummary.getCode(), summary);
//                }
//            }
//        }
//
//        for (int i = 0; i < listday7.size(); i++) {
//            CapitalFlowSummary capitalFlowSummary = listday7.get(i);
//            if (capitalFlowSummary != null) {
//                StockSummaryDTO summary = resultMap.get(capitalFlowSummary.getCode());
//                if (summary != null) {
//                    summary.setCapitalFlow7d(capitalFlowSummary.getSumCapitalFlow());
//                    summary.setMainCapitalFlow7d(capitalFlowSummary.getSumMainCapitalFlow());
//                    resultMap.put(capitalFlowSummary.getCode(), summary);
//                }
//            }
//        }

        List<StockSummaryDTO> sortedSummaries = resultMap.values().stream().collect(Collectors.toList());
        return sortedSummaries;
    }


    @Transactional
    public SysInfo upsertSysInfo(SysInfo sysInfo) {
        // 1. 根据业务唯一键code查询是否存在
        SysInfo existingSysInfo = sysInfoRepository.findBySysKeyAndTypes(sysInfo.getSysKey(), sysInfo.getTypes());
        System.out.println("upsertSysInfo== existingSysInfo=" + existingSysInfo);
        if (existingSysInfo != null) {
            // 2. 存在则更新（保留原有ID和创建时间，更新其他字段）
//            SysInfo updated = existingSysInfo;
            existingSysInfo.setSysValue(sysInfo.getSysValue()); // 更新名称
//            updated.setVersion(sysInfo.getVersion()); // 更新版本
            existingSysInfo.setCreateTime(LocalDateTime.now()); // 更新时间戳
            // 其他需要更新的字段...
//            sysInfoRepository.saveAndFlush(sysInfo.getSysKey(),sysInfo.getSysValue());
            sysInfoRepository.updateBySysKeyAndTypes(sysInfo.getSysKey(), sysInfo.getTypes(), sysInfo.getSysValue());
            System.out.println("upsertSysInfo== updateByKey");
            wencaiServiceTools.init();
            return existingSysInfo;
        } else {
            // 3. 不存在则新增（设置创建时间）
            System.out.println("upsertSysInfo== saveAndFlush");
            return sysInfoRepository.saveAndFlush(sysInfo);
        }
    }

}
